package ro.dandanciu.webgallery.ui.filters;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLDecoder;

import javax.imageio.ImageIO;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;

import ro.dandanciu.webgallery.domainmodel.Gallery;
import ro.dandanciu.webgallery.domainmodel.User;
import ro.dandanciu.webgallery.exceptions.ApplicationException;
import ro.dandanciu.webgallery.services.GalleryServices;
import ro.dandanciu.webgallery.services.locator.ServicesLocator;
import ro.dandanciu.webgallery.ui.general.Constants;
import ro.dandanciu.webgallery.util.PropertyReader;
import ro.dandanciu.webgallery.util.SettingsReader;

/**
 * 
 * @author Dan Danciu
 *	TODO make all kind of decorators for images: 
 *		rotate clockwise, rotate anti-clockwise, etc ...
 */
public class ImageSupplier implements Filter {
	private static final String ROTATE_PARAMETER = "rotate";
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(ImageSupplier.class);
	private static final String PUBLIC_URL_PART = "/public";
	
	public static String imagesBase = null;

	public void destroy() {}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain filterChain) throws IOException, ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("doFilter(ServletRequest, ServletResponse, FilterChain) - start");
		}
		
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		
		User user = (User) httpRequest.getSession().getAttribute(Constants.SESSION_USER_KEY);
		String url = URLDecoder.decode(httpRequest.getRequestURI(), "UTF8").replace("/WebGallery/image", "");
		String rotationString = httpRequest.getParameter(ROTATE_PARAMETER);
		int rotations = 0;
		if (rotationString != null) {
			try {
				rotations = Integer.parseInt(rotationString);
			} catch (NumberFormatException e) {
				logger.error(e);
				rotations = 0;			
			}
		}
		try {
			if (user != null && url.startsWith(user.getUserId())) {
				processPrivateImage(response, user, url, rotations);
			} else if (url.startsWith(PUBLIC_URL_PART)) {
				processPublicImage(response, url, rotations);
			} else {
				logger.info("Authorization failed, no user found in session!");
			}
		} catch (Exception e) {
			logger.error(e);
			response.getWriter().write("Unable to get image, reason: " + e.getMessage());
		}

		if (logger.isDebugEnabled()) {
			logger.debug("doFilter(ServletRequest, ServletResponse, FilterChain) - end");
		}
	}

	private void processPublicImage(ServletResponse response, String url,
			int rotations) throws IOException {
		logger.info("Public image request by url: " + url);
		
		GalleryServices galleryService =
				ServicesLocator.getInstance().getGalleryServices();
		
		// url should be: /public/OWNER/path/to/gallery/image.ext
		//               0/  1  /   2  /  .... / n - 2 /  n - 1    
		String[] tokens = url.split("/");
		
		String ownerUserId = tokens[2];
		String galleryName = tokens[tokens.length - 2];
		
		Gallery gallery = galleryService.getGalleryByOwnerAndName(ownerUserId, galleryName);
		
		if (gallery != null && gallery.isPublicAccess()) {
			String path = url.replaceAll(PUBLIC_URL_PART, "");
			path = imagesBase + path;
			streamImage(response.getOutputStream(), path, rotations);
		} else {
			throw new ApplicationException(); //TODO add reason
		}
		
		
	}

	private void processPrivateImage(ServletResponse response, User user,
			String url, int rotations) throws IOException {
		logger.info("User: " + user.getUserId() 
				+ ", url: " + url);
		
		String path = imagesBase + File.separator + user.getUserId()
				+ url;
		
		streamImage(response.getOutputStream(), path, rotations);
	}

	private void streamImage(OutputStream outputStream, String path, int rotations) {
		String extension = path.substring(path.lastIndexOf(".") + 1);
		
		try
		{
			File image = new File(path);
			//Create an image 200 x 200
		    BufferedImage bufferedImage = ImageIO.read(image);
		    
		    rotations = rotations % 4; //Sanity check
		    int times = 0;
		    Rotator rotator = null;
		    if (rotations > 0) {
		    	//rotate left
		    	times = rotations;
		    	rotator = new RotatorLeft();
		    } else if (rotations < 0) {
		    	//rotate right
		    	times = -rotations;
		    	rotator = new RotatorRight();		    
		    } else {
		    	//no rotations
		    	times = 0;
		    	rotator = null;
		    }
		    
		    try {
		    	for (int i  = times; i > 0 && rotator != null; i--) {
		    		bufferedImage = rotator.rotate(bufferedImage);
		    	}
		    } catch (Exception e) {
		    	logger.error(e);
		    }
		    
		    //Write the image
		    ImageIO.write(bufferedImage, extension, outputStream);
		}
		catch (IOException ioe)
		{
			logger.error(ioe);
		}
	}

	public void init(FilterConfig arg0) throws ServletException {
		imagesBase = new SettingsReader().getMessage("data.base");
	}
	
	private interface Rotator {
		BufferedImage rotate(BufferedImage bi);
	}
	
	private class RotatorLeft implements Rotator {	
		public BufferedImage rotate(BufferedImage bi)
		{
			int width = bi.getWidth();
			int height = bi.getHeight();
			
			BufferedImage biFlip = new BufferedImage(height, width, bi.getType());
			
			for(int i=0; i<width; i++)
				for(int j=0; j<height; j++)
					biFlip.setRGB(j, width - i - 1, bi.getRGB(i, j));
			
			return biFlip;
		}
	}
	
	private class RotatorRight implements Rotator {
		public BufferedImage rotate(BufferedImage bi)
		{
			int width = bi.getWidth();
			int height = bi.getHeight();

			BufferedImage biFlip = new BufferedImage(height, width, bi.getType());

			for(int i=0; i<width; i++)
				for(int j=0; j<height; j++) {
					//logger.debug("i: " + i + ", j: " + j);
					biFlip.setRGB(height - j - 1, i, bi.getRGB(i, j));
				}

			return biFlip;
		}
	}

	public static String getImagesBase() {
		return imagesBase;
	}

}
